package ffte.core.streamed

import spinal.core._
import spinal.lib._

import ffte.types.{FixComplex,FixReal,VecComplex}
import ffte.property.{getFFTIW,getFFTOW}
import ffte.misc
import ffte.core.{widthed,StreamedBus}

/**
 * Streamed 2-point DFT implementation
 *
 * This class implements a streamed 2-point Discrete Fourier Transform using a
 * three-stage pipeline architecture. The 2-point DFT is the fundamental building
 * block for larger FFT decompositions and represents the simplest possible FFT.
 *
 * Mathematical Foundation:
 * For N=2, the DFT simplifies to:
 * X[0] = x[0] + x[1]  (DC component)
 * X[1] = x[0] - x[1]  (Nyquist frequency)
 *
 * Pipeline Architecture:
 * 1. Stage 1: Complex to Real-Imaginary conversion (C2RI)
 * 2. Stage 2: Real-imaginary 2-point DFT computation (ridft2)
 * 3. Stage 3: Real-Imaginary to Complex conversion (RI2C)
 *
 * Streaming Features:
 * - Flow control with ready/valid handshaking
 * - Fragment support for burst data transfers
 * - Group processing for parallel efficiency
 * - Fixed-point arithmetic with configurable scaling
 *
 * @param S Scaling shift amount for fixed-point overflow prevention
 */
class sdft2(val S: Int) extends streamedComponents with widthed {
    /** Standard streaming I/O interface */
    val io = iogen

    /**
     * Stage 1: Complex to Real-Imaginary converter
     *
     * Converts input complex numbers to real-imaginary vector format
     * for optimized arithmetic processing in the DFT core.
     */
    val c2ri = new C2RI(G, dW)

    /**
     * Stage 2: Real-Imaginary 2-point DFT core
     *
     * Implements the actual 2-point DFT computation on real-imaginary
     * format data using addition and subtraction operations.
     */
    val ri2 = new ridft2(G, S, dW, oW)

    /**
     * Stage 3: Real-Imaginary to Complex converter
     *
     * Converts the computed real-imaginary results back to
     * standard complex number format for output.
     */
    val ri2c = new RI2C(G, oW)

    /**
     * Pipeline connections between stages
     *
     * Data flows through the three stages in sequence:
     * Complex → Real-Imaginary → DFT → Real-Imaginary → Complex
     */
    c2ri.io.q >> ri2.io.d   // Stage 1 to Stage 2
    ri2.io.q >> ri2c.io.d   // Stage 2 to Stage 3

    /**
     * External I/O connections
     *
     * Input data flows to the first stage and output data comes
     * from the final stage of the pipeline.
     */
    io.d >> c2ri.io.d      // Input to Stage 1
    io.q << ri2c.io.q      // Output from Stage 3

    /**
     * Total pipeline delay
     *
     * Sum of delays through all three pipeline stages.
     * This represents the total latency from input to output.
     */
    def delay = c2ri.delay + ri2.delay + ri2c.delay
} 

/**
 * Real-Imaginary 2-point DFT core
 *
 * Implements the core 2-point DFT computation on real-imaginary format data.
 * This component performs the actual mathematical operations required for
 * the 2-point DFT using addition and subtraction.
 *
 * Mathematical Operations:
 * For each input pair (x[0], x[1]):
 * - X[0] = x[0] + x[1]  (DC component)
 * - X[1] = x[0] - x[1]  (Nyquist component)
 *
 * Architecture Features:
 * - Parallel processing of G sample groups
 * - Registered output for timing control
 * - Configurable scaling for overflow prevention
 * - Fixed-point arithmetic with precision management
 *
 * @param G Group size for parallel processing
 * @param shift Scaling shift amount for overflow prevention
 * @param dW Input data width in bits
 * @param oW Output data width in bits
 */
class ridft2(val G: Int, shift: Int, dW: Int, oW: Int) extends Area {
    val io = new Area {
        val d = StreamedBus(Vec(Vec(FixReal(dW), 2), G))
        val q = StreamedBus(Vec(Vec(FixReal(oW), 2), G))
    }

    /** Output registers for computation results */
    val q = RegInit(Vec(Vec(FixReal(dW + 1).zero, 2), G))

    /** Flow control: Direct backpressure propagation */
    io.d.ready := io.q.ready
    io.q.valid := io.d.fire

    /**
     * Core DFT computation
     *
     * Performs addition and subtraction operations for 2-point DFT.
     * Uses increased precision (dW+1) to prevent overflow during
     * intermediate computations.
     */
    when(io.d.fire) {
        for (i <- 0 until G) {
            q(i)(0) := io.d.payload.fragment(i)(0).resize(dW + 1) + io.d.payload.fragment(i)(1)
            q(i)(1) := io.d.payload.fragment(i)(0).resize(dW + 1) - io.d.payload.fragment(i)(1)
        }
    }

    /**
     * Output precision scaling
     *
     * Scales the computation results back to the target output width
     * using the specified shift amount for overflow prevention.
     */
    for (i <- 0 until G) {
        io.q.payload.fragment(i)(0) := q(i)(0).fixTo(oW, shift)
        io.q.payload.fragment(i)(1) := q(i)(1).fixTo(oW, shift)
    }

    /** Last signal propagation for burst handling */
    io.q.payload.last := misc.delay(delay, 2).delayPulse(io.d.payload.last, io.d.fire)
    def delay = 1
}

/**
 * Complex to Real-Imaginary converter
 *
 * Converts complex input data to real-imaginary vector format for
 * optimized processing in the DFT core. Implements a ping-pong
 * buffering scheme for efficient streaming operation.
 *
 * Conversion Strategy:
 * - Extracts real and imaginary components from complex numbers
 * - Alternates between normal and swapped output ordering
 * - Uses registers for timing and flow control
 *
 * @param G Group size for parallel processing
 * @param dW Data width in bits
 */
class C2RI(G: Int, dW: Int) extends Area {
    val io = new Area {
        val d = StreamedBus(VecComplex(G, dW))
        val q = StreamedBus(Vec(Vec(FixReal(dW), 2), G))
    }

    /** Flow control: Direct backpressure propagation */
    io.d.ready := io.q.ready
    io.q.valid := io.d.fire

    /** Internal data buffers for conversion processing */
    val d = Vec(Vec(FixReal(dW), 2), G)
    val q = Vec(Vec(FixReal(dW), 2), G)

    /** Toggle flag for ping-pong buffering */
    val c = RegInit(False)

    /**
     * Toggle control logic
     *
     * Alternates the conversion mode on each transaction to
     * implement efficient ping-pong buffering. Resets on burst end.
     */
    when(io.d.fire) {
        when(io.d.payload.last) {
            c := False
        }.otherwise {
            c := ~c
        }
    }

    /**
     * Complex to real-imaginary conversion
     *
     * Extracts real and imaginary components and routes them
     * according to the current toggle state.
     */
    for (i <- 0 until G) {
        d(i)(0) := io.d.payload.fragment.d(i).re
        d(i)(1) := RegNextWhen(io.d.payload.fragment.d(i).im, io.d.fire)

        when(~c) {
            q(i)(0) := d(i)(0)
            q(i)(1) := d(i)(1)
        }.otherwise {
            q(i)(1) := d(i)(0)
            q(i)(0) := d(i)(1)
        }

        io.q.payload.fragment(i)(0) := RegNextWhen(q(i)(0), io.d.fire)
        io.q.payload.fragment(i)(1) := q(i)(1)
    }

    /** Last signal propagation */
    io.q.payload.last := misc.delay(delay, 2).delayPulse(io.d.payload.last, io.d.fire)
    def delay = 1
}

/**
 * Real-Imaginary to Complex converter
 *
 * Converts real-imaginary vector format back to standard complex format.
 * Implements the inverse operation of C2RI with similar ping-pong buffering.
 *
 * Conversion Strategy:
 * - Combines real and imaginary components into complex numbers
 * - Alternates between normal and swapped input ordering
 * - Uses registers for timing and flow control
 *
 * @param G Group size for parallel processing
 * @param dW Data width in bits
 */
class RI2C(G: Int, dW: Int) extends Area {
    val io = new Area {
        val d = StreamedBus(Vec(Vec(FixReal(dW), 2), G))
        val q = StreamedBus((VecComplex(G, dW)))
    }

    /** Flow control: Direct backpressure propagation */
    io.d.ready := io.q.ready
    io.q.valid := io.d.fire

    /** Internal data buffers for conversion processing */
    val d = Vec(Vec(FixReal(dW), 2), G)
    val q = Vec(Vec(FixReal(dW), 2), G)

    /** Toggle flag for ping-pong buffering */
    val c = RegInit(False)

    /**
     * Toggle control logic
     *
     * Synchronizes with C2RI converter to maintain proper
     * data ordering throughout the pipeline.
     */
    when(io.d.fire) {
        when(io.d.payload.last) {
            c := False
        }.otherwise {
            c := ~c
        }
    }

    /**
     * Real-imaginary to complex conversion
     *
     * Combines real and imaginary components into complex numbers
     * according to the current toggle state.
     */
    for (i <- 0 until G) {
        d(i)(0) := io.d.payload.fragment(i)(0)
        d(i)(1) := RegNextWhen(io.d.payload.fragment(i)(1), io.d.fire)

        when(~c) {
            q(i)(0) := d(i)(0)
            q(i)(1) := d(i)(1)
        }.otherwise {
            q(i)(1) := d(i)(0)
            q(i)(0) := d(i)(1)
        }

        io.q.payload.fragment.d(i).re := RegNextWhen(q(i)(0), io.d.fire)
        io.q.payload.fragment.d(i).im := q(i)(1)
    }

    /** Last signal propagation */
    io.q.payload.last := misc.delay(delay, 2).delayPulse(io.d.payload.last, io.d.fire)
    def delay = 1
}


